/* ! \file i2csw.c \brief Software I2C interface using port pins. */
// *****************************************************************************
//
//File Name:'i2csw.c'
// Title:Software I2C interface using port pins
// Author:Pascal Stang
// Created:11 / 22 / 2000
// Revised:5 / 2 / 2002
// Version:1.1
// Target MCU:Atmel AVR series
// Editor Tabs:4
//
//This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org / licenses / gpl.txt
//
//*****************************************************************************

#include <avr/io.h>

#include "i2csw.h"

#define SCLPORT PORTG
#define SCLDDR  DDRG	
#define SCL	5

#define SDAPORT	PORTG
#define SDADDR	DDRG
#define SDA	2
#define SDAPIN  PING	


// Standard I2C bit rates are:
//100 KHz for slow speed
// 400 KHz for high speed

#define QDEL  nrk_spin_wait_us(5)        // i2c quarter-bit delay
#define HDEL  nrk_spin_wait_us(10)       // i2c half-bit delay
			//i2c quarter - bit delay
// #define QDEL    asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop");
			// i2c half - bit delay
// #define HDEL    asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop");

//#define I2C_SDL_LO      cbi( SDAPORT, SDA)
//#define I2C_SDL_HI      sbi( SDAPORT, SDA)
//#define I2C_SCL_LO      cbi( SCLPORT, SCL);
//#define I2C_SCL_HI      sbi( SCLPORT, SCL);
#define I2C_SDL_LO      (SDAPORT &= ~(1<<SDA))
#define I2C_SDL_HI      (SDAPORT |= (1<<SDA))
#define I2C_SCL_LO      (SCLPORT &= ~(1<<SCL))
#define I2C_SCL_HI      (SCLPORT |= 1<<SCL)

#define I2C_SCL_TOGGLE  HDEL; I2C_SCL_HI; HDEL; I2C_SCL_LO;
#define I2C_START       I2C_SDL_LO; QDEL; I2C_SCL_LO;
#define I2C_STOP        HDEL; I2C_SCL_HI; QDEL; I2C_SDL_HI; HDEL;

#define SDA_OUTPUT		(SDADDR |= (1<<SDA))
#define SDA_INPUT		(SDADDR &= ~(1<<SDA))
#define SDA_GET_VALUE		(!!(SDAPIN & (1<<SDA)))	
		/*
		void i2ct(void)
		{
		HDEL; I2C_SCL_HI; HDEL; I2C_SCL_LO;
		}
		
		void i2cstart(void)
		{
		I2C_SDL_LO; QDEL; I2C_SCL_LO;
		}
		
		void i2cstop(void)
		{
		HDEL; I2C_SCL_HI; QDEL; I2C_SDL_HI; HDEL;
		}
		
		
		#define I2C_SCL_TOGGLE  i2ct();
		#define I2C_START       i2cstart();
		#define I2C_STOP        i2cstop();
		*/

int8_t i2cPutbyte(uint8_t  b) {
		int             i;

		for (i = 7; i >= 0; i--) {
			if (b & (1 << i))
				I2C_SDL_HI;
			else
				I2C_SDL_LO; //address bit
			I2C_SCL_TOGGLE; //clock HI, delay, then LO
		}

		I2C_SDL_HI; //leave SDL HI
		SDA_INPUT;
		//		cbi(SDADDR, SDA); //change direction to input on SDA line(may not be needed)
		HDEL;
		I2C_SCL_HI; //clock back up
		//b = inb(SDAPIN) & (1 << SDA);
		b = SDA_GET_VALUE;
		//get the ACK bit

		HDEL;
		I2C_SCL_LO;
		//not really ? ?
		//sbi(SDADDR, SDA);
		SDA_OUTPUT;
		//change direction back to output
		HDEL;
		return (b == 0);
		//return ACK value
}


uint8_t i2cGetbyte(uint8_t last)
{
	int             i;
	uint8_t c, b = 0;

	I2C_SDL_HI;
	//make sure pullups are ativated
	//cbi(SDADDR, SDA);
	//SDA_OUPUT;
	//change direction to input on SDA line(may not be needed)
	SDA_INPUT;
	for (i = 7; i >= 0; i--) {
		HDEL;
		I2C_SCL_HI;
		//clock HI
		//c = inb(SDAPIN) & (1 << SDA);
		c = SDA_GET_VALUE;
		b <<= 1;
		if (c)
			b |= 1;
		HDEL;
		I2C_SCL_LO;
		//clock LO
	}

	//sbi(SDADDR, SDA);
	SDA_OUTPUT;
	//change direction to output on SDA line

	if (last)
		I2C_SDL_HI; //set NAK
	else
		I2C_SDL_LO; //set ACK

	I2C_SCL_TOGGLE; //clock pulse
	I2C_SDL_HI; //leave with SDL HI
	return b;  //return received byte
}


		// ************************
		//*I2C public functions *
		//************************

		//!Initialize I2C communication
void  i2cInit(void) {
		SDADDR |= 1<<SDA;
		SCLDDR |= 1<<SCL;
		//sbi(SDADDR, SDA); //set SDA as output
		//sbi(SCLDDR, SCL); //set SCL as output
		I2C_SDL_HI; //set I / O state and pull - ups
		I2C_SCL_HI; //set I / O state and pull - ups
}

	              //!Send a byte sequence on the I2C bus
int8_t i2cSend(uint8_t device, uint8_t subAddr, uint8_t length, uint8_t* data) {
		I2C_START;
		i2cPutbyte(device);
		//send DEVICE address
		i2cPutbyte(subAddr);
		//and the subaddress

		// send the data
		while (length--)
			i2cPutbyte(*data++);

		I2C_SDL_LO;
		//clear data line and
		I2C_STOP;
		//send STOP transition
}

//!Retrieve a byte sequence on the I2C bus
int8_t i2cReceive(uint8_t  device, uint8_t  subAddr, uint8_t  length, uint8_t  * data) {
  int j = length;
  uint8_t  *p = data;

	I2C_START;
	i2cPutbyte(device);
	//send DEVICE address
	i2cPutbyte(subAddr);
	//and the subaddress
	HDEL;
	I2C_SCL_HI;
	I2C_START;
	i2cPutbyte(device | READ);
	//resend DEVICE, with READ bit set

	// receive data bytes
	while(j--)
		*p++ = i2cGetbyte(j == 0);

	I2C_SDL_LO; //clear data line and
	I2C_STOP; //send STOP transition
}

